#include "Player.h"

//Complete forward declarations
#include "StaticModel.h"
#include "GraphicsInterface.h"
#include "VirtualCamera.h"
#include <math.h>				// sin, cos

Player::Player()
{
	//Init health to full.
	health = 6;
	
	Invunerable = false;

	Attacking = false;

	InvunTimer = INITIAL_INVUL_TIME;
	AttackingTimer = INITIAL_ATTACK_TIME;

	Damage = 1;
}


Player::~Player()
{
	// Clean up the memory (NOT THE CAMERA)
	SafeDelete(playerModel);
}


void Player::Init(ID3D11Device* device, 
		XMFLOAT3 position, XMFLOAT3 rotation, XMFLOAT3 scale,
		VirtualCamera* camera, Terrain* gameTerrain)
{
	//store camera and intialises position
	Camera.Intialise(camera, scale);

	//Store a pointer to the games terrain so we can uise the GetHeight() function
	terrain = gameTerrain;

	// Alloc memory for the player model
	playerModel = new StaticModel();

	// Load the model from file
	playerModel->CreateModelThroughAssimp(device, 
		std::string(PLAYER_MODEL_FILE_PATH), std::string(PLAYER_MODEL_TEXTURE_FILE_PATH),
		true, true, true, false, false); 

	// Get the rotation in radians, hacked it together, not exact!
	rotation.y =  (PI + (TO_RADS * Camera.GetCamera()->GetCameraRotationDegreesYaw()));
	// Make the players position on the y axis equal to the terrain they are currently occupying
	position.y = (terrain->GetHeight(position.x, position.z)) + MODEL_Y_OFFSET;

	// Create the matrix for the player model.
	playerModel->SetWorldMatrix(position, rotation, scale);
	Camera.Update(position,rotation.y);

	CurrentQuest = 0;

	//sets centre of hitbox to players centre
	HitBox.CenterW = playerModel->GetWorldPosition();

	//gets the extents of the local bounding box
	XMFLOAT3 temp = playerModel->GetAABBL().Extents;

	//scales extents
	temp.x *= (scale.x*2);
	temp.y *= (scale.y*2);
	temp.z *= (scale.z*2);

	//sets hitbox extents
	HitBox.ExtentsW = temp;

	//sets respawn points to players intail position
	RespawnPoint = playerModel->GetWorldPosition();
}


// Return the health of the player.
// Used within the game logic accordingly
int Player::GetHealth()
{
	return health;
}

void Player::SetHealth(int New)
{
	 health = New;
}

// Updates the player - location and so on.
void Player::Update(float delta)
{
	//checks quest for completion
	if(CurrentQuest)
	{
		if(CurrentQuest->GetIsCompleted())
		{
			CurrentQuest = 0;
		}
	}

	// Store the current position of the player model
	XMFLOAT3 playerPosition = playerModel->GetWorldPosition();
	XMFLOAT3 Velocity(0,0,0);
	// Get rotation of the model on the y-axis
	float playerAngle = playerModel->GetWorldRotation().y;

	// Get the new velocity based upon keyboard input
	Velocity = Player::GetInput(delta, Velocity);

	Camera.Update(playerModel->GetWorldPosition(), playerAngle);

	// Create a tempoary store for the velocity, we will use this to calculate
	// the direction in which the model is currently facing and append the
	// players position.
	XMFLOAT3 temp = Velocity;

	temp.z = (-Velocity.x * sinf(playerAngle)) + (Velocity.z * cosf(playerAngle));
	temp.x = (Velocity.x * cosf(playerAngle)) + (Velocity.z * sinf(playerAngle));
	Velocity = temp;

	// Get the current rotation of the player and store it
	temp = playerModel->GetWorldRotation();
	// Get the rotation in radians, hacked it together, not exact!
	temp.y = ((PI + (TO_RADS * Camera.GetCamera()->GetCameraRotationDegreesYaw())));

	// Make the players position on the y axis equal to the terrain they are currently occupying
	playerPosition.y = (terrain->GetHeight(playerPosition.x, playerPosition.z)) + MODEL_Y_OFFSET;

	// Only when the camera is not in free mode update the players position and rotations
	if(Camera.GetCameraMode() != FREE_MODE)
	{
		playerPosition.x += Velocity.x;
		playerPosition.y += Velocity.y;
		playerPosition.z += Velocity.z;
		playerModel->SetWorldPosition(playerPosition);
		playerModel->SetWorldRotation(temp);
	}

	// Update camera
	Camera.Update(playerModel->GetWorldPosition(), playerAngle);

	//updates quest and checks for failure
	if(CurrentQuest)
	{
		CurrentQuest->Update(delta, playerModel->GetWorldPosition());

		if(CurrentQuest->GetFailed())
		{
			CurrentQuest = 0;
		}
	}

	//updates hitbox
	HitBox.CenterW = playerModel->GetWorldPosition();

	//if invunerable count down every frame until timer runs out
	if(Invunerable)
	{
		InvunTimer -= delta;

		if(InvunTimer <= 0)
		{
			Invunerable = false;
		}
	}

	//if attacking count down every frame until timer runs out
	if(Attacking)
	{
		AttackingTimer -= delta;

		if(AttackingTimer <= 0)
		{
			Attacking = false;
		}
	}
}

// Returns the current position of the player
XMFLOAT3 Player::GetPosition()
{
	//Ask the StaticModel instance where we are in world space. 
	return playerModel->GetWorldPosition();
}

void Player::AddToBatch(GraphicsInterface* gi)
{
	//Render the player model.
	gi->AddModelToBatch(playerModel);
}

// Check for keyboard input and return the new velocity
XMFLOAT3 Player::GetInput(float frametime, XMFLOAT3 velocity)
{
	// Get input and Move Player
	// Have to reverse directions as the model is spawned in facing the -z direction
	// and is adjusted in the game scene by 180deg clockwise.
	if( GetAsyncKeyState('W') & 0x8000 )
	{
		// Adjust the velocity
		velocity.z -= 5.0f*frametime* PLAYER_CAMERA_SPEED_MULTIPLER;

		if(Camera.GetCameraMode() == FREE_MODE)
		{
			Camera.GetCamera()->Walk(-1*velocity.z);
		}

	}

	if( GetAsyncKeyState('S') & 0x8000 )
	{
		velocity.z += 5.0f*frametime* PLAYER_CAMERA_SPEED_MULTIPLER;

		if(Camera.GetCameraMode() == FREE_MODE)
		{
			Camera.GetCamera()->Walk(-1*velocity.z);
		}
	}

	if( GetAsyncKeyState('A') & 0x8000 )
	{
		velocity.x += 5.0f*frametime* PLAYER_CAMERA_SPEED_MULTIPLER;

		if(Camera.GetCameraMode() == FREE_MODE)
		{
			Camera.GetCamera()->Strafe(-1*velocity.x);
		}
	}

	if( GetAsyncKeyState('D') & 0x8000 )
	{
		velocity.x -= 5.0f*frametime* PLAYER_CAMERA_SPEED_MULTIPLER;

		if(Camera.GetCameraMode() == FREE_MODE)
		{
			Camera.GetCamera()->Strafe(-1*velocity.x);
		}
	}

	// We want roo to jump here
	if( GetAsyncKeyState(VK_SPACE) & 0x8000 )
	{
		velocity.y += frametime* PLAYER_CAMERA_SPEED_MULTIPLER;
	}

	if( GetAsyncKeyState(VK_RBUTTON) & 0x8000  && !Attacking)
	{
		Attacking = true;
		AttackingTimer = INITIAL_ATTACK_TIME;
	}

	if(GetAsyncKeyState('I') & 0x8000 )
	{
		Camera.ChangeCameraMode(FIRST_PERSON);
	}

	if(GetAsyncKeyState('O') & 0x8000 )
	{
		Camera.ChangeCameraMode(THIRD_PERSON);
	}

	if(GetAsyncKeyState('P') & 0x8000 )
	{
		Camera.ChangeCameraMode(FREE_MODE);
	}

	return velocity;
}

AABB Player::GetHitBox()
{
	return HitBox;
}

AbstractQuest* Player::GetCurrentQuest()
{
	return CurrentQuest;
}

void Player::SetCurrentQuest(AbstractQuest* quest)
{
	if(quest)
	{
		CurrentQuest = quest;
	}
}

bool Player::GetInvunerable()
{
	return Invunerable;
}

void Player::SetInvunerable(bool invun)
{
	Invunerable = invun;

	if(invun)
	{
		InvunTimer = INITIAL_INVUL_TIME;
	}
}

bool Player::GetAttacking()
{
	return Attacking;
}

void Player::SetAttacking(bool attack)
{
	Attacking = attack;

	if(Attacking)
	{
		AttackingTimer = INITIAL_ATTACK_TIME;
	}

}

void Player::Respawn()
{
	playerModel->SetWorldPosition(RespawnPoint);
}


int Player::GetDamage()
{
	return Damage;
}

void Player::SetDamage(int damage)
{
	Damage = damage;
}